Extending the DSGE functionalities =================================== RISE is designed to be pluggable at every step of the standard DSGE workflow -- solve, filter, optimize, sample. The user can replace any of those steps with a custom routine without touching the rest of the toolbox. This chapter lists the hooks that the ``@dsge`` model object exposes for that purpose. For users who need to plug in a whole *alternative computational strategy* (occbin-style piecewise linearisation, recursive local linearisation, k-th order Taylor projection, ...), the broader interface is described in :doc:`../Paradigms/Extending RISE through paradigms`; the hooks below are the lighter, per-routine extension points. Extracting the structural form ---------------------------------- The matrices of the parsed and solved model are reachable via ``get`` on the model object: ``get(m, 'definitions')``, ``get(m, 'parameters')``, ``get(m, 'endogenous')``, ``get(m, 'exo_list')``, ``get(m, 'obs_list')``, ``get(m, 'sstate')`` (steady state by regime), ``get(m, 'par_list')``, ``get(m, 'chain_list')`` (Markov chain names), and so on. The full list of accessor strings is documented on the ``get`` method of ``@dsge``. Extracting the solution ------------------------- After ``solve`` succeeds, the solution lives in ``obj.state_space{1}`` (one entry per parameterization) and includes the regime-specific transition matrices ``T``, the shock-impact matrices ``R``, and the steady state. The convenience ``map_solution`` method packs the state-space matrices into a struct keyed by Markov regime; together with ``get(obj, 'sstate')`` it is the canonical interface for any post-processing that does not want to depend on internal field layouts. Your own solver ------------------- Set the ``solver`` option to a function handle to replace the built-in fixed-point iteration with a user routine:: m = set(m, 'solver', @my_solver); The signature is documented in :doc:`Local Approximation/Stochastic solution by perturbation`. The bundled solvers (``rise_1``, ``mfi``, ``mn``, ``mnk``, ``mfi_full``, ``loose_commitment``) all conform to that signature; the simplest way to write your own is to copy one of them and modify the inner loop. Your own filtering algorithm ----------------------------- .. index:: filtering The Kalman / smoother family is replaceable through the ``kf_user_algo`` option:: m = set(m, 'kf_user_algo', @my_filter); m = set(m, 'kf_user_algo', {@my_filter, opt1, opt2, ...}); The expected signature is ``[LogLik, Incr, retcode, Filters] = my_filter(syst, y, U, z, options, vargs{:})`` where ``syst`` is the solved state-space (or model object when the filter needs more than the default), ``y`` and ``U`` are the observable and exogenous data, ``z`` is a callable that maps states to observables at each time, and ``options`` carries the filter options. A ``*`` prefix on the function name (``'*my_filter'``) tells RISE that the filter wants the model object rather than the state-space struct -- useful when the user algorithm needs to call back into the toolbox. The bundled ``switching_unscented_kalman_filter``, ``switching_divided_difference_filter`` and ``switching_cubature_kalman_filter`` are the reference implementations; see :doc:`Filtering/Nonlinear filtering`. Your own optimizer ------------------- Posterior maximization, mode search and global optimisation are routed through the ``optimizer`` option, which accepts a function handle or a ``{handle, options-struct}`` cell:: m = set(m, 'optimizer', @my_opt); m = set(m, 'optimizer', {@my_opt, struct('MaxIter', 500)}); The expected signature is ``[x, fval, exitflag, output] = my_opt(objfun, x0, lb, ub, options)``. The bundled optimizers in ``m/optimizers/`` (``wcsminwel``, ``wnewrat``, ``wgmhmaxlik``, ``bee_gate``, the ``+globalopt`` family ``rise_de`` / ``rise_cma_es`` / ``rise_abc`` / ``rise_lshade`` / ``rise_jso`` / ...) all conform to that signature and serve as templates; see :doc:`../Estimation/Posterior maximization`. Your own Likelihood function ------------------------------ The Bayesian estimation entry point routes the *likelihood* through ``obj.routines.likelihood``. To plug in a custom likelihood -- e.g. a DSGE-VAR likelihood or a moment-matching pseudo-likelihood -- set that field after constructing the model and before calling ``estimate``:: obj = add_to_routines(obj, 'likelihood', @my_likelihood); The expected signature is ``[LogLik, Incr, retcode, obj, filtration] = my_likelihood(params, obj)``. The default is the Kalman-based likelihood for a linearised model (the configured nonlinear filter is invoked automatically at higher order), and the DSGE-VAR variant ``likelihood_dsge_var`` shows how a non-Kalman likelihood plugs in. Relation to Indirect Inference ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``indirect_inference`` entry point (see :doc:`../Estimation/Indirect Inference`) is a thin wrapper around the same optimization machinery, but with a *criterion* function in place of the likelihood. The user supplies the criterion via ``indirect_inference(m, myobjective, 'priors', priors)``, where ``[critmin, retcode] = myobjective(m)`` returns the (weighted) distance between the model's auxiliary statistics and their data counterparts. Replace ``myobjective`` to switch between GMM (theoretical moments), SMM (sample moments from simulation) and IRF matching (simulated vs empirical IRFs) without touching anything else in the pipeline. Your own posterior sampler ------------------------------ The MCMC samplers (``rsamplers.rwmh``, ``rsamplers.imh``, ``rsamplers.slice``, ``rsamplers.apt``) are independent of the model object -- they take a target-log-density function handle, an initial point, bounds, and an options struct. To use a custom sampler, build a small wrapper that calls ``pull_objective(m)`` to obtain the target handle and then drives the sampler. The ``rsamplers_*`` files in ``m/+rsamplers/`` are the reference implementations and demonstrate the contract that ``utils.sampling`` infrastructure (progress monitor, checkpointing, diagnostics) expects. Where this is not enough ------------------------- The hooks above replace one *routine* at a time. If the goal is to plug in a whole solution paradigm -- piecewise-linear OccBin, recursive local linearisation, k-th order Taylor projection -- the per-routine extension surface is too small, because such paradigms need to own the solution representation as well. The :doc:`../Paradigms/Extending RISE through paradigms` chapter covers that broader extension surface and is the right place to start for a new computational strategy.